package org.jsola.hr.service.impl;

import com.alibaba.fastjson.JSON;
import org.jsola.core.Page;
import org.jsola.hr.common.DateUtils;
import org.jsola.hr.common.ExampleKit;
import org.jsola.hr.constant.ExtraType;
import org.jsola.hr.constant.HrConstants;
import org.jsola.hr.dao.IAttendPunchCalibrationDAO;
import org.jsola.hr.dto.AttendPunchCalibrationAddDTO;
import org.jsola.hr.dto.AttendPunchCalibrationUpdateDTO;
import org.jsola.hr.dto.AttendPunchRecordUpdateDTO;
import org.jsola.hr.dto.attendrule.AttendRuleLocationAddDTO;
import org.jsola.hr.dto.attendrule.AttendRulePunchTimeAddDTO;
import org.jsola.hr.dto.attendschedule.AttendPeriodDTO;
import org.jsola.hr.entity.*;
import org.jsola.hr.exception.HrException;
import org.jsola.hr.query.AttendExtraJsonQuery;
import org.jsola.hr.query.AttendPunchCalibrationQuery;
import org.jsola.hr.query.AttendPunchRecordQuery;
import org.jsola.hr.query.AttendPunchTimeQuery;
import org.jsola.hr.service.*;
import org.jsola.hr.vo.AttendPunchCalibrationListVO;
import org.jsola.hr.vo.AttendPunchCalibrationVO;
import org.jsola.hr.vo.AttendPunchRecordVO;
import org.jsola.orm.filter.Order;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import tk.mybatis.mapper.entity.Example;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 考勤打卡校准记录表
 *
 * @author zhr
 */
@Service("hrAttendPunchCalibrationServiceImpl")
public class AttendPunchCalibrationServiceImpl implements IAttendPunchCalibrationService {

    @Autowired
    private IAttendPunchCalibrationDAO attendPunchCalibrationDAO;

    @Autowired
    private IAttendPunchRecordService attendPunchRecordService;

    @Autowired
    private IAttendPlanService attendPlanService;

    @Autowired
    private IAttendPunchTimeService attendPunchTimeService;

    @Autowired
    private IAttendExtraJsonService attendExtraJsonService;

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public AttendPunchCalibrationVO save(AttendPunchCalibrationAddDTO attendPunchCalibrationAddDTO, TokenUser tokenUser) {
        // 当前补卡顺序
        Integer number = attendPunchCalibrationAddDTO.getNumber();
        //矫正的时间
        Date punchTime = attendPunchCalibrationAddDTO.getPunchTime();
        if (punchTime == null) {
            throw new HrException("请输入矫正时间");
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
        //根据id查找要校准的数据
        AttendPunchRecordVO attendPunchRecordVO = attendPunchRecordService.selectById(attendPunchCalibrationAddDTO.getPunchRecordId(), tokenUser.getSiteId());
        AttendPunchRecordUpdateDTO attendPunchRecordUpdateDTO = new AttendPunchRecordUpdateDTO();
        //查找相对应的参保方案
        AttendPlanDO attendPlanDO = attendPlanService.selectDOById(attendPunchRecordVO.getAttendPlanId(), tokenUser.getSiteId());
        //休息时间
        Integer restTime = 0;
        if (attendPlanDO != null) {
            AttendPunchTimeQuery attendPunchTimeQuery = new AttendPunchTimeQuery();
            attendPunchTimeQuery.setAttendPlanId(attendPlanDO.getId());
            List<AttendPunchTimeDO> attendPunchTime = attendPunchTimeService.selectDO(attendPunchTimeQuery, tokenUser.getSiteId());
            if (!CollectionUtils.isEmpty(attendPunchTime)) {
                AttendPunchTimeDO punch = attendPunchTime.get(0);
                String periodJson = punch.getPeriodJson();
                if (!StringUtils.isEmpty(periodJson)) {
                    List<AttendPeriodDTO> attendPeriod = JSON.parseArray(periodJson, AttendPeriodDTO.class);
                    if (!CollectionUtils.isEmpty(attendPeriod)) {
                        if (attendPunchRecordVO.getPunchType() == 3 || attendPunchRecordVO.getPunchType() == 1) {
                            String startPeriod;
                            if(number == 3){
                                startPeriod = attendPeriod.get(1).getStartPeriod();
                            } else {
                                startPeriod = attendPeriod.get(0).getStartPeriod();
                            }
                            Integer latestOnPunchTime = Integer.parseInt(punch.getLatestOnPunchTime());
                            Integer integer = this.timeCompare(simpleDateFormat.format(punchTime), startPeriod + ":00", latestOnPunchTime);
                            if (integer == 1) {
                                attendPunchRecordUpdateDTO.setPunchStatus(2);
                                try {
                                    Integer minutesDiff = DateUtils.getMinutesDiff(
                                            simpleDateFormat.parse(startPeriod + ":00"),
                                            simpleDateFormat.parse(simpleDateFormat.format(punchTime)));
                                    attendPunchRecordUpdateDTO.setMinute(Math.abs(minutesDiff));
                                } catch (Exception e) {
                                    throw new HrException("迟到时间校准失败");
                                }
                            } else {
                                attendPunchRecordUpdateDTO.setPunchStatus(1);
                            }
                        } else {
                            String endPeriod;
                            if(number == 4){
                                endPeriod = attendPeriod.get(1).getEndPeriod();
                            } else {
                                endPeriod = attendPeriod.get(0).getEndPeriod();
                            }
                            int earliestOffPunchTime = Integer.parseInt(punch.getEarliestOffPunchTime());
                            Integer integer = this.timeCompare(simpleDateFormat.format(punchTime), endPeriod + ":00", earliestOffPunchTime * -1);
                            if (integer == -1) {
                                attendPunchRecordUpdateDTO.setPunchStatus(3);
                                try {
                                    Integer minutesDiff = DateUtils.getMinutesDiff(
                                            simpleDateFormat.parse(endPeriod + ":00"),
                                            simpleDateFormat.parse(simpleDateFormat.format(punchTime)));
                                    attendPunchRecordUpdateDTO.setMinute(Math.abs(minutesDiff));
                                } catch (Exception e) {
                                    e.printStackTrace();
                                    throw new HrException("早退时间校准失败");
                                }
                            } else {
                                attendPunchRecordUpdateDTO.setPunchStatus(1);
                            }
                        }
                    }
                }
            } else {
                attendPunchRecordUpdateDTO.setPunchStatus(1);
            }
            String punchLocationJson = attendPlanDO.getPunchLocationJson();
            if (!StringUtils.isEmpty(punchLocationJson)) {
                List<AttendRuleLocationAddDTO> locationList = JSON.parseArray(punchLocationJson, AttendRuleLocationAddDTO.class);
                if (!CollectionUtils.isEmpty(locationList)) {
                    attendPunchRecordUpdateDTO.setLocation(locationList.get(0).getPositionName());
                }
            }
            //计算中间的休息时间
            restTime = this.restTime(attendPlanDO.getId(), tokenUser.getSiteId());
        } else {
            attendPunchRecordUpdateDTO.setPunchStatus(1);
        }
        //查询上班时间，补全下班时间的工作时长
        AttendPunchRecordQuery attendPunchRecordQuery = new AttendPunchRecordQuery();
        attendPunchRecordQuery.setYear(attendPunchRecordVO.getYear());
        attendPunchRecordQuery.setMonth(attendPunchRecordVO.getMonth());
        attendPunchRecordQuery.setDay(attendPunchRecordVO.getDay());
        attendPunchRecordQuery.setCompanyId(attendPunchRecordVO.getCompanyId());
        attendPunchRecordQuery.setEmpInfoId(attendPunchRecordVO.getEmpInfoId());
        if (attendPunchRecordVO.getPunchType() == 4 || attendPunchRecordVO.getPunchType() == 2) {
            attendPunchRecordQuery.setPunchType(1);
            List<Order> orderList = new ArrayList<>();
            // 升序查询上班打卡记录
            orderList.add(new Order("standardTime", false));
            attendPunchRecordQuery.setOrders(orderList);
            List<AttendPunchRecordDO> attend = attendPunchRecordService.selectDO(attendPunchRecordQuery, tokenUser.getSiteId());
            if (!CollectionUtils.isEmpty(attend)) {
                Integer minutesDiff = 0;
                if (attend.size() == 1) {
                    // 一次打卡走原来的逻辑
                    minutesDiff = DateUtils.getMinutesDiff(attend.get(0).getPunchTime(), punchTime);
                } else {
                    // 两次打卡需要判断此次需要匹配的上班打卡时间
                    if (number != null && number == 2) {
                        // 补齐第一次下班卡  找到第一次的上班记录
                        minutesDiff = DateUtils.getMinutesDiff(attend.get(0).getPunchTime(), punchTime);
                    } else if (number != null &&  number == 4) {
                        // 补齐第二次下班卡  找到第二次的上班记录
                        minutesDiff = DateUtils.getMinutesDiff(attend.get(1).getPunchTime(), punchTime);
                    }
                }
                attendPunchRecordUpdateDTO.setWorkTime(Math.abs(minutesDiff) - restTime);
            } else {
                throw new HrException("请补全上班考勤");
            }
            attendPunchRecordUpdateDTO.setPunchType(2);
        }
        else {
            attendPunchRecordQuery.setPunchType(2);
            List<AttendPunchRecordDO> attend = attendPunchRecordService.selectDO(attendPunchRecordQuery, tokenUser.getSiteId());
            if (!CollectionUtils.isEmpty(attend)) {
                int minutesDiff = -1;
                AttendPunchRecordUpdateDTO attendPunchRecordUpdateWorkTime = new AttendPunchRecordUpdateDTO();
                if (number != null && number == 1) {
                    minutesDiff = DateUtils.getMinutesDiff(attend.get(0).getPunchTime(), punchTime);
                    attendPunchRecordUpdateWorkTime.setId(attend.get(0).getId());
                } else if (number != null && number == 3 && attend.size() > 1) {
                    minutesDiff = DateUtils.getMinutesDiff(attend.get(1).getPunchTime(), punchTime);
                    attendPunchRecordUpdateWorkTime.setId(attend.get(1).getId());
                }
                if (minutesDiff != -1) {
                    attendPunchRecordUpdateWorkTime.setWorkTime(Math.abs(minutesDiff) - restTime);
                    attendPunchRecordService.update(attendPunchRecordUpdateWorkTime, tokenUser);
                }
            }
            attendPunchRecordUpdateDTO.setPunchType(1);
        }
        attendPunchRecordUpdateDTO.setId(attendPunchCalibrationAddDTO.getPunchRecordId());
        attendPunchRecordUpdateDTO.setPunchTime(punchTime);
        attendPunchRecordUpdateDTO.setIsCalibration(true);
        attendPunchRecordService.update(attendPunchRecordUpdateDTO, tokenUser);
        // 转DO
        AttendPunchCalibrationDO attendPunchCalibrationDO = attendPunchCalibrationAddDTO.to(AttendPunchCalibrationDO.class);
        // 保存
        attendPunchCalibrationDO = save(attendPunchCalibrationDO, tokenUser.getSiteId(), tokenUser.getUserId());
        return attendPunchCalibrationDO.to(AttendPunchCalibrationVO.class);
    }

    /**
     * 两个时间比较
     *
     * @param date1 时间1
     * @param date2 时间2
     * @return 比较结果
     */
    private Integer timeCompare(String date1, String date2, Integer threshold) {
        SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
        Calendar c1 = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();
        try {
            c1.setTime(formatter.parse(date1));
            c2.setTime(formatter.parse(date2));
            if(!ObjectUtils.isEmpty(threshold)){
                c2.add(Calendar.MINUTE, threshold);
            }
        } catch (ParseException e) {
            e.printStackTrace();
            throw new HrException("校验状态失败");
        }
        //比date1小返回-1 	相等返回 0  比date1大 返回1
        Integer result = c1.compareTo(c2);
        return result;
    }

    @Override
    public Integer restTime(String attendId, String siteId) {
        List<AttendRulePunchTimeAddDTO> punchTimeList = new ArrayList<>();
        Integer time = 0;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = new Date();
        // 查询额外数据
        AttendExtraJsonQuery attendExtraJsonQuery = new AttendExtraJsonQuery();
        attendExtraJsonQuery.setAttendPalnId(attendId);
        attendExtraJsonQuery.setExtraType(ExtraType.ATTEND_PUNCH_TIME.getValue());
        List<AttendExtraJsonDO> attendExtraJsonDOList = attendExtraJsonService.selectDO(attendExtraJsonQuery, siteId);
        if (!CollectionUtils.isEmpty(attendExtraJsonDOList)) {
            for (AttendExtraJsonDO attendExtraJsonDO : attendExtraJsonDOList) {
                punchTimeList.addAll(JSON.parseArray(attendExtraJsonDO.getJsonData(), AttendRulePunchTimeAddDTO.class));
            }
        }
        if (!CollectionUtils.isEmpty(punchTimeList)) {
            for (AttendRulePunchTimeAddDTO attendRulePunchTimeAddDTO : punchTimeList) {
                AttendPeriodDTO restPeriod = attendRulePunchTimeAddDTO.getRestPeriod();
                List<AttendPeriodDTO> periodList = attendRulePunchTimeAddDTO.getPeriodList();
                if (!CollectionUtils.isEmpty(periodList) && periodList.get(0).getHasRestPeriod() != null && periodList.get(0).getHasRestPeriod()) {
                    if (restPeriod != null && 0 == restPeriod.getType()) {
                        String startTime = sdf.format(date) + " " + restPeriod.getStartPeriod() + ":00";
                        String endTime = sdf.format(date) + " " + restPeriod.getEndPeriod() + ":00";
                        try {
                            time += Math.abs(DateUtils.getMinutesDiff(simpleDateFormat.parse(startTime), simpleDateFormat.parse(endTime)));
                        } catch (Exception e) {
                            e.printStackTrace();
                            throw new HrException("休息时间计算失败");
                        }
                    }
                }
            }
        }
        return time;
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int update(AttendPunchCalibrationUpdateDTO attendPunchCalibrationUpdateDTO, TokenUser tokenUser) {
        //转DO
        AttendPunchCalibrationDO attendPunchCalibrationDO = attendPunchCalibrationUpdateDTO.to(AttendPunchCalibrationDO.class);
        //根据主键更新，只更新非null值
        return updateByIdSelective(attendPunchCalibrationDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int deleteByIds(TokenUser tokenUser, String... attendPunchCalibrationIds) {
        return logicalDeleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), (Object[]) attendPunchCalibrationIds);
    }


    @Override
    public AttendPunchCalibrationVO selectById(String attendPunchCalibrationId, String siteId) {
        AttendPunchCalibrationDO attendPunchCalibrationDO = selectDOById(attendPunchCalibrationId, siteId);
        if (attendPunchCalibrationDO == null) {
            return null;
        }
        return attendPunchCalibrationDO.to(AttendPunchCalibrationVO.class);
    }

    @Override
    public List<AttendPunchCalibrationListVO> select(AttendPunchCalibrationQuery attendPunchCalibrationQuery, String siteId) {
        List<AttendPunchCalibrationDO> attendPunchCalibrationDOList = selectDO(attendPunchCalibrationQuery, siteId);
        if(CollectionUtils.isEmpty(attendPunchCalibrationDOList)) {
            return attendPunchCalibrationDOList == null ? null : new ArrayList<>();
        }
        return attendPunchCalibrationDOList.stream()
                .map(attendPunchCalibrationDO -> attendPunchCalibrationDO.to(AttendPunchCalibrationListVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public int selectCount(AttendPunchCalibrationQuery attendPunchCalibrationQuery, String siteId) {
        Example example = buildExample(attendPunchCalibrationQuery,siteId);
        return attendPunchCalibrationDAO.selectCountByExample(example);
    }

    @Override
    public Page<AttendPunchCalibrationListVO> selectPage(AttendPunchCalibrationQuery attendPunchCalibrationQuery, String siteId) {
        Example example = buildExample(attendPunchCalibrationQuery,siteId);
        Page<AttendPunchCalibrationDO> page = attendPunchCalibrationDAO.selectPageByExample(example,
                attendPunchCalibrationQuery.getPageNo(),
                attendPunchCalibrationQuery.getPageSize());

        return page.to(AttendPunchCalibrationListVO.class);
    }

    @Override
    public AttendPunchCalibrationDO selectDOById(String attendPunchCalibrationId, String siteId) {
        return listById(attendPunchCalibrationId, siteId);
    }

    @Override
    public List<AttendPunchCalibrationDO> selectDO(AttendPunchCalibrationQuery attendPunchCalibrationQuery, String siteId) {
        Example example = buildExample(attendPunchCalibrationQuery,siteId);
        return attendPunchCalibrationDAO.selectByExample(example);
    }


    /**
     * 根据查询参数，构建example

     * @param attendPunchCalibrationQuery 查询参数
     * @param siteId 所属站点id
     * @return example
     */
    private Example buildExample(AttendPunchCalibrationQuery attendPunchCalibrationQuery, String siteId) {
        Example example = new Example(AttendPunchCalibrationDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        if (attendPunchCalibrationQuery.getPunchRecordId() != null) {
            example.and().andEqualTo("punchRecordId", attendPunchCalibrationQuery.getPunchRecordId());
        }
        if (attendPunchCalibrationQuery.getRemark() != null) {
            example.and().andEqualTo("remark", attendPunchCalibrationQuery.getRemark());
        }
        if (attendPunchCalibrationQuery.getHisPunchStatus() != null) {
            example.and().andEqualTo("hisPunchStatus", attendPunchCalibrationQuery.getHisPunchStatus());
        }
        if (attendPunchCalibrationQuery.getHisPunchTimeStart() != null &&
                attendPunchCalibrationQuery.getHisPunchTimeEnd() != null) {
            example.and().andBetween("hisPunchTime", attendPunchCalibrationQuery.getHisPunchTimeStart(),
                    attendPunchCalibrationQuery.getHisPunchTimeEnd());
        }
        if (attendPunchCalibrationQuery.getCalibrationMethod() != null) {
            example.and().andEqualTo("calibrationMethod", attendPunchCalibrationQuery.getCalibrationMethod());
        }
        // 排序
        ExampleKit.setExampleOrder(example,attendPunchCalibrationQuery.getOrders());
        return example;
    }
}




